﻿using System;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using Perspective.Wpf3D.Primitives;
using System.Windows;

namespace Perspective.Wpf3D.Sculptors
{
    /// <summary>
    /// 组成圆台型的所有点和三角形集合
    /// A class to handle points and triangles of a 3D frustum of conical model.
    /// Default radius is 1.0.
    /// By default, the direction of the cone is the Z axis, and the length is 1.0.
    /// </summary>
    public class FrustumConicalSculptor : Sculptor
    {
        internal const double DefaultTopRadius = 0.5;
        internal const double DefaultBottomRadius = 1.0;
        internal const double DefaultHeight = 1.0;
        private int _circumferenceSideCount;
        private double _initialAngle;
        private double _roundingRate;

        private double _topRadius = DefaultTopRadius;
        private double _bottomRadius = DefaultBottomRadius;
        private double _height = DefaultHeight;

          /// <summary>
        /// Initializes a new instance of ConicalSculptor.
        /// </summary>
        public FrustumConicalSculptor()
        {
        }

         /// <summary>
        /// Initializes a new instance of ConicalSculptor.
        /// </summary>
        /// <param name="circumferenceSideCount">Model circumference side count.</param>
        /// <param name="initialAngle">Angle between the axis [origin - first point] and the X-axis, in degrees.</param>
        /// <param name="roundingRate">Angle rounding rate. The value must be comprized between 0.0 and 0.5.</param>
        public FrustumConicalSculptor(int circumferenceSideCount, double initialAngle, double roundingRate)
        {
            Initialize(circumferenceSideCount, initialAngle, roundingRate);
        }

         /// <summary>
        /// Initializes a new instance of ConicalSculptor.
        /// </summary>
        /// <param name="height">Concial height.</param>
        /// <param name="circumferenceSideCount">Model circumference side count.</param>
        /// <param name="initialAngle">Angle between the axis [origin - first point] and the X-axis, in degrees.</param>
        /// <param name="roundingRate">Angle rounding rate. The value must be comprized between 0.0 and 0.5.</param>
        public FrustumConicalSculptor(double height,int circumferenceSideCount, double initialAngle, double roundingRate)
        {
            Initialize(height,circumferenceSideCount, initialAngle, roundingRate);
        }

        /// <summary>
        /// Initializes a new instance of ConicalSculptor.
        /// </summary>
        /// <param name="topRadius">Concial Top Radius</param>
        /// <param name="bottomRadius">Concial Bottom Radius</param>
        /// <param name="height">Model height.</param>
        /// <param name="circumferenceSideCount">Model circumference side count.</param>
        /// <param name="initialAngle">Angle between the axis [origin - first point] and the X-axis, in degrees.</param>
        /// <param name="roundingRate">Angle rounding rate. The value must be comprized between 0.0 and 0.5.</param>
        public FrustumConicalSculptor(double topRadius, double bottomRadius, double height, int circumferenceSideCount, double initialAngle, double roundingRate)
        {
            Initialize(topRadius,bottomRadius, height, circumferenceSideCount, initialAngle, roundingRate);
        }

         /// <summary>
        /// Initializes an existing instance of ConicalSculptor.
        /// </summary>
        /// <param name="circumferenceSideCount">Model circumference side count.</param>
        /// <param name="initialAngle">Angle between the axis [origin - first point] and the X-axis, in degrees.</param>
        /// <param name="roundingRate">Angle rounding rate. The value must be comprized between 0.0 and 0.5.</param>
        public void Initialize(int circumferenceSideCount, double initialAngle, double roundingRate)
        {
            Initialize(DefaultHeight,circumferenceSideCount,initialAngle,roundingRate);
        }


        /// <summary>
        /// Initializes an existing instance of ConicalSculptor.
        /// </summary>
        /// <param name="height">Model height.</param>
        /// <param name="circumferenceSideCount">Model circumference side count.</param>
        /// <param name="initialAngle">Angle between the axis [origin - first point] and the X-axis, in degrees.</param>
        /// <param name="roundingRate">Angle rounding rate. The value must be comprized between 0.0 and 0.5.</param>
        public void Initialize(double height, int circumferenceSideCount, double initialAngle, double roundingRate)
        {
            Initialize(DefaultTopRadius,DefaultBottomRadius,height,circumferenceSideCount,initialAngle,roundingRate);
        }

        /// <summary>
        /// Initializes an existing instance of ConicalSculptor.
        /// </summary>
        /// <param name="topRadius">Concial Top Radius</param>
        /// <param name="bottomRadius">Concial Bottom Radius</param>
        /// <param name="height">Model height.</param>
        /// <param name="circumferenceSideCount">Model circumference side count.</param>
        /// <param name="initialAngle">Angle between the axis [origin - first point] and the X-axis, in degrees.</param>
        /// <param name="roundingRate">Angle rounding rate. The value must be comprized between 0.0 and 0.5.</param>
        public void Initialize(double topRadius,double bottomRadius, double height, int circumferenceSideCount, double initialAngle, double roundingRate)
        {
            _circumferenceSideCount = circumferenceSideCount;
            _initialAngle = initialAngle;
            _roundingRate = roundingRate;
            _height = height;
            _bottomRadius = bottomRadius;
            _topRadius = topRadius;
        }

        private PolygonSculptor _ps1;
        private PolygonSculptor _ps2;


        /// <summary>
        /// Initializes the Points and Triangles collections.
        /// Called By Sculptor.BuildMesh()
        /// </summary>
        protected override void CreateTriangles()
        {
            Vector3D heightVector = new Vector3D(0, 0, _height);
            _ps1 = new PolygonSculptor(_bottomRadius, _circumferenceSideCount, _initialAngle);
            _ps1.RoundingRate = _roundingRate;
            _ps1.BuildTriangles(TriangleSideKind.Back);
            int circumferencePointCount = _ps1.Points.Count;
            CopyFrom(_ps1);

            _ps2 = new PolygonSculptor(_topRadius, _circumferenceSideCount, _initialAngle);
            _ps2.RoundingRate = _roundingRate;
            _ps2.RoundCorner();
            _ps2.BuildMesh();
            _ps2.Transform(new TranslateTransform3D(0.0, 0.0, _height));
            _ps2.Center = new Point3D(
               _ps2.Center.X,
               _ps2.Center.Y,
               _ps2.Center.Z + _height);
            _ps2.BuildTriangles(TriangleSideKind.Front);
            CopyFrom(_ps2);

            for (int j = 0; j < circumferencePointCount; j++)
            {
                int index1 = j;                         //底部圆周坐标点
                int index2 = circumferencePointCount + j;             //顶部圆周坐标点
                int indexLimit = circumferencePointCount + circumferencePointCount-1;

                if (index2 < indexLimit)
                {
                    this.Triangles.Add(new Point3DTriplet(Points[index2], Points[index1], Points[index2 + 1]));
                    this.Triangles.Add(new Point3DTriplet(Points[index2 + 1], Points[index1], Points[index1 + 1]));
                }
                else
                {
                    this.Triangles.Add(new Point3DTriplet(Points[index2], Points[index1], Points[circumferencePointCount]));
                    this.Triangles.Add(new Point3DTriplet(Points[circumferencePointCount], Points[index1], Points[0]));
                }
            }
        }

        /// <summary>
        /// A method for building the TextureCoordinates collection of the mesh.
        /// </summary>
        internal override void MapTexture()
        {
            foreach (Point3DTriplet pt in _ps1.Triangles)
            {
                foreach (Point3D p in pt.Points)
                {
                    Mesh.TextureCoordinates.Add(
                        new Point(p.X, -p.Y));
                }
            }

            foreach (Point3DTriplet pt in _ps2.Triangles)
            {
                foreach (Point3D p in pt.Points)
                {
                    Mesh.TextureCoordinates.Add(
                        new Point(p.X, -p.Y));
                }
            }
        }

    }
}
